#ifndef __C_NGISMODEL_MODEL_BEHAVIOR_H__
#define __C_NGISMODEL_MODEL_BEHAVIOR_H__

#include "IModelBehavior.h"
#include <string>
#include <map>
#include <algorithm>
#include "../tinyxml2.h"
using namespace tinyxml2;

namespace NGIS
{
	namespace Model
	{
		class CModelBehavior : public IModelBehavior
		{
		public:
			CModelBehavior()
			{

			}

			~CModelBehavior()
			{
				for (int i=0; i<mModelDatasetItemList.size(); i++)
				{
					ModelDatasetItem temp_item = mModelDatasetItemList[i];
					if (temp_item.datasetItem)
					{
						temp_item.datasetItem->release();
					}
				}
				mModelDatasetItemList.clear();
			}

			bool loadFromXml(const XMLElement* pModelClassElement);

			bool formatToXml(XMLElement* pModelClassElement)
			{
				XMLDocument* doc = pModelClassElement->GetDocument();
				XMLElement* behaviorEle = doc->NewElement("Behavior");

				XMLElement* relatedDatasetsEle = doc->NewElement("RelatedDatasets");
				XMLElement* stateGroupEle = doc->NewElement("StateGroup");

				for (int i=0; i<mModelDatasetItemList.size(); i++)
				{
					ModelDatasetItem temp_item = mModelDatasetItemList[i];
					XMLElement* temp_ele = doc->NewElement("DatasetItem");
					temp_ele->SetAttribute("name", temp_item.datasetName.c_str());
					if (temp_item.datasetItemType == EMDIT_EXTERNAL)
					{
						temp_ele->SetAttribute("type", "external");
						temp_ele->SetAttribute("externalId", temp_item.externalId.c_str());
						temp_ele->SetAttribute("description", temp_item.datasetItemDescription.c_str());
					}
					else if (temp_item.datasetItemType == EMDIT_INTERNAL)
					{
						temp_ele->SetAttribute("type", "internal");
						temp_ele->SetAttribute("description", temp_item.datasetItemDescription.c_str());
						if (temp_item.datasetItem)
							temp_item.datasetItem->FormatToXmlElement(temp_ele);
					}
					else if (temp_item.datasetItemType == EMDIT_RAW)
					{
						temp_ele->SetAttribute("type", "raw");
						temp_ele->SetAttribute("description", temp_item.datasetItemDescription.c_str());
					}

					relatedDatasetsEle->LinkEndChild(temp_ele);
				}

				XMLElement* states_ele = doc->NewElement("States");
				stateGroupEle->LinkEndChild(states_ele);
				for (int i=0; i<mModelStateList.size(); i++)
				{
					ModelState temp_state = mModelStateList[i];
					XMLElement* temp_state_ele = doc->NewElement("State");
					temp_state_ele->SetAttribute("id", temp_state.stateId.c_str());
					temp_state_ele->SetAttribute("name", temp_state.stateName.c_str());
					if (temp_state.stateType == EMST_BASIC)
						temp_state_ele->SetAttribute("type", "basic");
					else if (temp_state.stateType == EMST_GROUP)
						temp_state_ele->SetAttribute("type", "group");
					else
						temp_state_ele->SetAttribute("type", "unknown");
					temp_state_ele->SetAttribute("description", temp_state.stateDecription.c_str());

					//////////////////////////////////////////////////////////////////////////
					for (int iEvent = 0; iEvent<temp_state.modelEvents.size(); iEvent++)
					{
						ModelEvent temp_event = temp_state.modelEvents[iEvent];
						EModelEventType eventType = temp_event.eventType;
						XMLElement* event_ele = doc->NewElement("Event");
						event_ele->SetAttribute("name", temp_event.eventName.c_str());
						if (eventType == EModelEventType::EMET_RESPONSE)
						{
							event_ele->SetAttribute("type", "response");
							XMLElement* param_ele = doc->NewElement("ResponseParameter");
							param_ele->SetAttribute("datasetReference", temp_event.datasetReference.c_str());
							param_ele->SetAttribute("description", temp_event.parameterDescription.c_str());
							event_ele->LinkEndChild(param_ele);
						}
						else if (eventType == EModelEventType::EMET_NORESPONSE)
						{
							event_ele->SetAttribute("type", "noresponse");
							XMLElement* param_ele = doc->NewElement("DispatchParameter");
							param_ele->SetAttribute("datasetReference", temp_event.datasetReference.c_str());
							param_ele->SetAttribute("description", temp_event.parameterDescription.c_str());
							event_ele->LinkEndChild(param_ele);
						}
						else if (eventType == EModelEventType::EMET_CONTROL)
						{
							event_ele->SetAttribute("type", "control");
							XMLElement* param_ele = doc->NewElement("ControlParameter");
							param_ele->SetAttribute("datasetReference", temp_event.datasetReference.c_str());
							param_ele->SetAttribute("description", temp_event.parameterDescription.c_str());
							event_ele->LinkEndChild(param_ele);
						}
						event_ele->SetAttribute("optional", temp_event.optional);
						event_ele->SetAttribute("description", temp_event.eventDescription.c_str());

						temp_state_ele->LinkEndChild(event_ele);
					}
					
					states_ele->LinkEndChild(temp_state_ele);
				}

				XMLElement* state_transition_ele = doc->NewElement("StateTransitions");
				stateGroupEle->LinkEndChild(state_transition_ele);
				std::vector<ModelStateTransition>::iterator it = mModelStateTransitionList.begin();
				std::vector<ModelStateTransition>::iterator end = mModelStateTransitionList.end();
				for (; it!=end; it++)
				{
					std::string temp_from = it->fromState.stateId;
					std::string temp_to = it->toState.stateId;
					XMLElement* temp_ele = doc->NewElement("Add");
					temp_ele->SetAttribute("from", temp_from.c_str());
					temp_ele->SetAttribute("to", temp_to.c_str());

					state_transition_ele->LinkEndChild(temp_ele);
				}

				behaviorEle->LinkEndChild(relatedDatasetsEle);
				behaviorEle->LinkEndChild(stateGroupEle);

				pModelClassElement->LinkEndChild(behaviorEle);
				return true;
			}
		public:
			//////////////////////////////////////Dataset Declarations////////////////////////////////////
			virtual bool addModelDatasetItem(ModelDatasetItem& pDataset)
			{
				for (int i=0; i<mModelDatasetItemList.size(); i++)
				{
					if (mModelDatasetItemList[i].datasetName == pDataset.datasetName)
					{
						return false;
					}
				}
				mModelDatasetItemList.push_back(pDataset);
				return true;
			}

			virtual bool removeModelDatasetItem(ModelDatasetItem& pDataset) 
			{
				std::vector<ModelDatasetItem>::iterator it = mModelDatasetItemList.begin();
				std::vector<ModelDatasetItem>::iterator end = mModelDatasetItemList.end();
				for (; it != end; it++)
				{
					if (it->datasetName == pDataset.datasetName)
					{
						mModelDatasetItemList.erase(it);
						return true;
					}
				}
				return false;
			}

			virtual int getModelDatasetItemCount() 
			{
				return mModelDatasetItemList.size();
			}

			virtual bool getModelDatasetItem(int idx, ModelDatasetItem& pDataset) 
			{
				if (idx<0 || idx>=mModelDatasetItemList.size()) return false;
				pDataset = mModelDatasetItemList[idx];
				return true;
			}

			virtual bool getModelDatasetItem(std::string pName, ModelDatasetItem& pDataset) 
			{
				for (int i=0; i<mModelDatasetItemList.size(); i++)
				{
					if (mModelDatasetItemList[i].datasetName ==pName)
					{
						pDataset = mModelDatasetItemList[i];
						return true;
					}
				}
				return false;
			}

			virtual bool updateModelDatasetItem(int idx, ModelDatasetItem& pDataset)
			{
				if (idx<0 || idx>=mModelDatasetItemList.size()) return false;
				if (mModelDatasetItemList[idx].datasetItem != pDataset.datasetItem)
				{
					mModelDatasetItemList[idx].datasetItem->release();
				}
				mModelDatasetItemList[idx] = pDataset;
				return true;
			}

			//////////////////////////////////////Model State Group////////////////////////////////////
			virtual bool addModelState(ModelState& pState) 
			{
				for (int i=0; i<mModelStateList.size(); i++)
				{
					if (mModelStateList[i].stateId == pState.stateId)
					{
						return false;
					}
				}
				mModelStateList.push_back(pState);
				return true;
			}

			virtual bool removeModelState(ModelState& pState)
			{
				std::vector<ModelState>::iterator it = mModelStateList.begin();
				std::vector<ModelState>::iterator end = mModelStateList.end();
				for (; it != end; it++)
				{
					if (it->stateId == pState.stateId)
					{
						mModelStateList.erase(it);
						return true;
					}
				}
				return false;
			}

			virtual int getModelStateCount()
			{
				return mModelStateList.size();
			}

			virtual bool getModelState(int idx, ModelState& pState) 
			{
				if (idx<0 || idx>=mModelStateList.size()) return false;
				pState = mModelStateList[idx];
				return true;
			}

			virtual bool getModelState(std::string pStateId, ModelState& pState) 
			{
				for (int i=0; i<mModelStateList.size(); i++)
				{
					if (mModelStateList[i].stateId ==pStateId)
					{
						pState = mModelStateList[i];
						return true;
					}
				}
				return false;
			}

			virtual bool updateModelState(int idx, ModelState& pState)
			{
				if (idx<0 || idx>=mModelStateList.size()) return false;
				mModelStateList[idx] = pState;
				return true;
			}

			virtual bool updateModelState(std::string pStateId, ModelState& pState)
			{
				for (int i=0; i<mModelStateList.size(); i++)
				{
					if (mModelStateList[i].stateId ==pStateId)
					{
						mModelStateList[i] = pState;
						return true;
					}
				}
				return false;
			}

			/////////////////////////////////////Model State Transition/////////////////////////////////////
			virtual bool addModelStateTransition(std::string pFromStateId, std::string pToStateId) 
			{
				ModelState fromState, toState; 
				bool flag1 = getModelState(pFromStateId, fromState);
				bool flag2 = getModelState(pToStateId, toState);
				if (flag1 && flag2)
				{
					for (int i=0; i<mModelStateTransitionList.size(); i++)
					{
						if (mModelStateTransitionList[i].fromState.stateId == pFromStateId &&
							mModelStateTransitionList[i].toState.stateId == pToStateId)
						{
							return false;
						}
					}
					ModelStateTransition temp_transition;
					temp_transition.fromState = fromState;
					temp_transition.toState = toState;
					mModelStateTransitionList.push_back(temp_transition);
					return true;
				}
				return false;
			}

			virtual bool addModelStateTransition(ModelState& pFromState, ModelState& pToState)
			{
				return addModelStateTransition(pFromState.stateId, pToState.stateId);
			}

			virtual bool removeModelStateTransition(std::string pFromStateId, std::string pToStateId)
			{
				std::vector<ModelStateTransition>::iterator it = mModelStateTransitionList.begin();
				std::vector<ModelStateTransition>::iterator end = mModelStateTransitionList.end();
				for (; it!=end; it++)
				{
					if (it->fromState.stateId == pFromStateId && it->toState.stateId == pToStateId)
					{
						mModelStateTransitionList.erase(it);
						return true;
					}
				}
				return false;
			}

			virtual bool removeModelStateTransition(ModelState& pFromState, ModelState& pToState) 
			{
				return removeModelStateTransition(pFromState.stateId, pToState.stateId);
			}

			virtual int getModelStateTransitionCount() 
			{
				return mModelStateTransitionList.size();
			}

			virtual bool getModelStateTransition(int idx, ModelState& pFromState, ModelState& pToState)
			{
				if (idx<0 || idx>= mModelStateTransitionList.size()) return false;
				std::vector<ModelStateTransition>::iterator it = mModelStateTransitionList.begin();
				for (int i=0; i<idx; i++)
				{
					it ++;
				}
				pFromState = it->fromState;
				pToState = it->toState;
				return true;
			}

			virtual bool getModelStateTransition(int idx, ModelStateTransition& pStateTransition)
			{
				if (idx<0 || idx>= mModelStateTransitionList.size()) return false;
				pStateTransition = mModelStateTransitionList[idx];
				return true;
			}

			virtual bool existModelStatetTransition(std::string pFromStateId, std::string pToStateId) 
			{
				std::vector<ModelStateTransition>::iterator it = mModelStateTransitionList.begin();
				std::vector<ModelStateTransition>::iterator end = mModelStateTransitionList.end();
				for (; it!=end; it++)
				{
					if (it->fromState.stateId == pFromStateId && it->toState.stateId == pToStateId)
					{
						return true;
					}
				}
				return false;
			}

			virtual bool existModelStatetTransition(ModelState& pFromState, ModelState& pToState) 
			{
				return existModelStatetTransition(pFromState.stateId, pToState.stateId);
			}

			virtual bool updateModelStateTransition(int idx, std::string pFromStateId, std::string pToStateId)
			{
				if (idx<0 || idx>= mModelStateTransitionList.size()) return false;
				std::vector<ModelStateTransition>::iterator it = mModelStateTransitionList.begin();
				for (int i=0; i<idx; i++)
				{
					it ++;
				}
				removeModelStateTransition(it->fromState, it->toState);
				addModelStateTransition(pFromStateId, pToStateId);
				return true;
			}

			virtual bool compareOther(IModelBehavior* pBehavior, std::string& obj, std::string& name)
			{
				if (mModelDatasetItemList.size() != pBehavior->getModelDatasetItemCount())
				{
					obj = "RelatedDatasets";
					name = "DatasetItemCount";
					return false;
				}
				for (int i=0; i<mModelDatasetItemList.size(); i++)
				{
					ModelDatasetItem temp_item1 = mModelDatasetItemList[i];
					ModelDatasetItem temp_item2;
					pBehavior->getModelDatasetItem(i, temp_item2);
					if (temp_item1.compareOther(temp_item2)==false)
					{
						obj = "DatasetItem";
						name = temp_item1.datasetName + "<->" + temp_item2.datasetName;
						return false;
					}
				}
				if (mModelStateList.size() != pBehavior->getModelStateCount())
				{
					obj = "States";
					name = "StateCount";
					return false;
				}
				for (int i=0; i<mModelStateList.size(); i++)
				{
					ModelState temp_state1 = mModelStateList[i];
					ModelState temp_state2;
					pBehavior->getModelState(i, temp_state2);
					if (temp_state1.compareOther(temp_state2)==false)
					{
						obj = "State";
						name = temp_state1.stateName + "<->" + temp_state2.stateName;
						return false;
					}
				}
				if (mModelStateTransitionList.size() != pBehavior->getModelStateTransitionCount())
				{
					obj = "StateTransitions";
					name = "StateTransitionCount";
					return false;
				}
				for (int i=0; i<mModelStateTransitionList.size(); i++)
				{
					ModelStateTransition temp_trans1 = mModelStateTransitionList[i];
					ModelStateTransition temp_trans2;
					pBehavior->getModelStateTransition(i, temp_trans2);
					if (temp_trans1.compareOther(temp_trans2)==false)
					{
						obj = "StateTransition";
						name = temp_trans1.fromState.stateId + "<->" + temp_trans2.fromState.stateId;
						return false;
					}
				}
				return true;
			}
		private:
			std::vector<ModelDatasetItem> mModelDatasetItemList;
			std::vector<ModelState> mModelStateList;
			std::vector<ModelStateTransition> mModelStateTransitionList;
		};
	}
}

#endif

